home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Programming Sound Cards
/
Programming Sound Cards.iso
/
sound_06
/
play.c
< prev
next >
Wrap
Text File
|
1995-01-01
|
10KB
|
429 lines
/*
PLAY.C
29-jun-88, Marc Savary, Ad Lib Inc.
Demo file to play the AdLib MIDI music file ( *.MUS) with the *.SND
timbre bank file.
This simple driver use the PC's timer-0 for timing. (see TIMER.ASM )
See "convert.c" for *.MUS file structure.
This file and the file BANK.C (timbre bank manager) MUST BE compiled
WITHOUT stack overflow check. (-v option of Lattice C Compiler,
-Gs for Microsoft)
If compiling with Lattice, 'LATTICE' must be defined for this file
and BANK.C.
With Microsoft, use the following ('MICROSOFT' must be defined for
this file and BANK.C):
masm timer.asm
cl -DMICROSOFT -AS -J -Zi -Zp1 -Ox -Gs -c play.c bank.c adlib.c
link play.obj adlib.obj bank.obj timer.obj
*/
#include <stdio.h>
#include <fcntl.h>
#include "convert.h"
#include "bank.h"
#ifdef MICROSOFT
#define UNSIGNED_CHAR unsigned char
#endif
#ifdef LATTICE
#define UNSIGNED_CHAR char
#endif
BankPtr bank; /* pointer to instrument bank */
char * musPtr; /* pointer to first data byte of melody */
struct MusHeader * headPtr; /* pointer to header of .MUS file */
char musRunning; /* != 0 if music is playing */
UNSIGNED_CHAR status; /* running status byte */
char volume[ NR_VOICES]; /* actual volume of all voices */
long tickCount; /* time counter, for information only */
unsigned delay; /* length of last delay */
#ifdef LATTICE
extern char * getmem();
extern int allmem();
extern int rlsmem();
#endif
#ifdef MICROSOFT
#include <malloc.h>
#include <string.h>
#define rlsmem(x,y) free(x)
#define getmem(x) malloc(x)
#define setmem(x,y,z) memset(x,z,y)
#define movmem(x,y,z) memmove(y,x,z)
#define max(x,y) ((x > y) ? x:y)
#endif
/*
Simple demonstration.
Syntax: play <musfile.mus> <bankfile.snd>
*/
main( argc, argv)
int argc;
char * argv[];
{
struct MusHeader mH;
int meloFile;
char * music;
unsigned len;
BankPtr theBank;
if( argc < 3) {
fprintf( stderr, "\nUSE: play <musfile.mus> <bankfile.snd>");
exit( 1);
}
/* Perform some initialisations ... */
Init();
if( NULL == ( theBank = OpenBank( argv[ 2], 0))) {
fprintf( stderr, "\nUnable to open timbre bank file '%s'.", argv[ 2]);
Terminate();
exit( 1);
}
if( !LoadBank( theBank)) {
fprintf( stderr, "\nError while reading timbre bank.");
Terminate();
exit( 1);
}
meloFile = open( argv[ 1], O_RDONLY + O_RAW);
if( -1 == meloFile) {
fprintf( stderr, "\nUnable to open music file '%s'.", argv[ 1]);
Terminate();
exit( 1);
}
/* read the music file's header: */
read( meloFile, &mH, sizeof( struct MusHeader));
len = mH.dataSize;
music = (char *) getmem( (unsigned) len);
if( music == NULL) {
fprintf( stderr, "\nMemory allocation error.");
exit( 1);
}
/* load all the data in memory: */
read( meloFile, music, len);
/* Start playing: */
StartMelo( &mH, music, len, theBank);
/* wait until end.... */
WaitEndMelo();
/* uninstall the clock driver: */
Terminate();
rlsmem( music, len);
CloseBank( theBank);
} /* main() */
/*
Wait until the end of melody ( musRunning == 0)
*/
WaitEndMelo()
{
static unsigned measure = 0, beat = 0;
unsigned m, b, c, i;
/* cprintf( "\nPress ESC key to stop"); */
while( musRunning) {
if( kbhit()) {
c = getch();
if( c == 0x1b)
StopMelo();
}
#ifdef DEBUG
m = tickCount / (headPtr->beatMeasure * headPtr->tickBeat);
b = tickCount / headPtr->tickBeat;
if( m != measure) {
printf( "\nMeasure: %03d ", m);
measure = m;
}
if( b != beat) {
printf( "+ ");
beat = b;
}
#endif
}
} /* WaitEndMelo() */
/*
Initialize the driver.
*/
Init()
{
/* initalize the low-level sound-driver: */
if( !SoundColdInit( 0x388)) {
printf( "\nAdlib board not found!");
exit( 1);
}
/* allocate all the memory available:*/
#ifdef LATTICE
allmem();
#endif
/* install the clock driver: */
Clk_install();
}
/*
Uninstall the clock driver ...
*/
Terminate()
{
Clk_uninstall();
}
/*
Start playing a melody. Set some global pointers for the interrupt
routine. Set the tempo, sound mode & pitch bend range.
Reset volume of each voice. Start the clock driver with the first
delay ( >= 1)
*/
StartMelo( header, data, len, timBank)
struct MusHeader * header; /* pointer to header struc. of music file */
char * data; /* pointer to music data */
unsigned len; /* size of data */
BankPtr timBank; /* bank of timbres */
{
int i;
musPtr = data;
headPtr = header;
bank = timBank;
tickCount = 0;
for( i = 0; i < NR_VOICES; i++)
volume[ i] = 0;
SetMode( header->soundMode);
SetPitchRange( header->pitchBRange);
SetTempo( header->basicTempo, header->tickBeat);
StartTimeOut( 0);
delay = *musPtr++;
musRunning = 1;
/* NEVER START COUNT-DOWN WITH 0, since 0 means MAXIMUM delay!!! */
StartTimeOut( max( delay, 1));
} /* StartMelo() */
/*
Stop playing the melody. Send note-off to all voices and reset
the clock frequency to nominal ( 18.2 Hz).
*/
StopMelo()
{
int i;
musRunning = 0;
for( i = 0; i < NR_VOICES; i++)
NoteOff( i);
SetTempo( 0, 1);
} /* StopMelo() */
/*
Change the tempo.
Reload the timer-0 with the proper divider for generating
the appropriate frequency.
If tempo is zero, reprogram the counter for 18.2 Hz.
*/
SetTempo( tempo, tickBeat)
unsigned tempo; /* beats per minute */
unsigned tickBeat; /* ticks per beat */
{
long t1;
unsigned freq;
unsigned low, high, flags, count;
t1 = tickBeat * (long)tempo;
freq = t1 /60; /* interrupt rate needed */
if( !freq)
count = 0;
else {
/* make sure that frequency is >= 19 Hz, since counter min. output
frequency is 18.2 Hz: */
freq = freq < 19 ? 19 : freq;
/* compute counter divider: */
count = (1193180 /(long)freq);
}
/* and set the counter: */
SetClkRate( count);
} /* SetTempo() */
/*
Interrupt routine. Called by low-level clock driver when
the delay count has expired.
'musPtr' always points to an OVERFLOW BYTE or to the first byte AFTER
the timing byte.
When this routine is called, the active SS ( stack segment) is not
the original of the application, so take care.
This routine, and all others called by, must be compiled
without stack-overflow checking, since the SS has changed!!!
Return to caller the number of clock ticks to wait for before
the next call.
*/
unsigned TimeOut()
{
unsigned pitch, tempo, haut, vol;
UNSIGNED_CHAR newStatus;
int timbreDef[ TIMBRE_DEF_LEN], timbre;
int comm, id, integer, frac, voice = 1;
if( ! musRunning)
/* Music has not started or has been stopped, so wait the minimum delay ... */
return 1;
tickCount += delay;
do {
newStatus = *musPtr;
if( newStatus == OVERFLOW_BYTE) {
/* timing overflow ... */
musPtr++;
delay = OVERFLOW_BYTE;
break;
}
else if( newStatus == STOP_BYTE) {
StopMelo();
return 0; /* maximum delay ... */
}
else if( newStatus == SYSTEM_XOR_BYTE) {
/*
non-standard... this is a tempo multiplier:
data format: <F0> <7F> <00> <integer> <frac> <F7>
tempo = basicTempo * integerPart + basicTempo * fractionPart/128
*/
musPtr++;
id = *musPtr++;
comm = *musPtr++;
if( id != ADLIB_CTRL_BYTE || comm != TEMPO_CTRL_BYTE) {
/* unknown format ... skip all the XOR message */
musPtr -= 2;
while( *musPtr++ != EOX_BYTE)
;
}
else {
integer = *musPtr++;
frac = *musPtr++;
tempo = headPtr->basicTempo;
tempo = tempo * integer + (unsigned)(((long)tempo * frac) >> 7);
SetTempo( tempo, headPtr->tickBeat);
musPtr++; /* skip EOX_BYTE */
}
delay = *musPtr++;
}
else {
if( newStatus >= 0x80) {
musPtr++;
status = newStatus;
}
voice = (int) (status & 0x0f);
switch( status & 0xf0) {
case NOTE_ON_BYTE:
haut = *musPtr++;
vol = *musPtr++;
if( ! vol) {
NoteOff( voice);
}
else {
if( vol != volume[ voice]) {
SetVoiceVolume( voice, vol);
volume[ voice] = vol;
}
NoteOn( voice, haut);
}
break;
case NOTE_OFF_BYTE:
musPtr += 2;
NoteOff( voice);
break;
case AFTER_TOUCH_BYTE:
SetVoiceVolume( voice, *musPtr++);
break;
case PROG_CHANGE_BYTE:
timbre = *musPtr++;
if( GetTimbre( "", &timbre, timbreDef, bank)) {
SetVoiceTimbre( voice, timbreDef);
}
else
cprintf( "\nTimbre not found: %d", timbre);
break;
case PITCH_BEND_BYTE:
pitch = *musPtr++;
pitch += (unsigned)(*musPtr++) << 7;
SetVoicePitch( voice, pitch);
break;
case CONTROL_CHANGE_BYTE:
/* not implemented ... */
musPtr += 2;
break;
case CHANNEL_PRESSURE_BYTE:
/* not implemented ... */
musPtr++;
break;
default:
cprintf( "\nBad MIDI status byte: %d", status);
SkipToTiming();
break;
}
delay = *musPtr++;
}
} while( delay == 0);
if( delay == OVERFLOW_BYTE) {
delay = OVERFLOW;
if( *musPtr != OVERFLOW_BYTE)
delay += *musPtr++;
}
return delay;
}
/*
A bad status byte ( or unimplemented MIDI command) has been encontered.
Skip bytes until next timing byte followed by status byte.
*/
static SkipToTiming()
{
while( *musPtr < 0x80)
musPtr++;
if( *musPtr != OVERFLOW_BYTE)
musPtr--;
} /* SkipToTiming() */